package com.cx.redis;

import com.cx.util.SerializeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis调用工具类:提供基础的set\delete
 * https://www.cnblogs.com/mignet/p/Redis_Concurrent_generate_sequence_no.html
 * http://www.jianshu.com/p/7bf5dc61ca06
 * http://blog.csdn.net/liang_love_java/article/details/50497281
 *
 * https://blog.csdn.net/congcong68/article/details/52734735
 *
 * https://blog.csdn.net/i_vic/article/details/53081241
 *
 */
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate2;
    private static RedisUtil redisUtil;
    private static RedisTemplate<String, Object> redisTemplate;

    @PostConstruct
    public void init(){
        redisUtil = this;
        redisTemplate = this.redisTemplate2;
    }


    /**
     * 自增
     * @param key
     * @param time
     * @return
     */
    public static Long incr(String key,Long time){

//        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
// counter.set(value);
//		counter.expireAt(expireTime);
// Long increment = entityIdCounter.getAndIncrement();
//
//        if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始设置过期时间
//            entityIdCounter.expire(liveTime, TimeUnit.SECONDS);
//        }

        Long l = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.incr(key.getBytes());
            }
        });
        if (time!=null && l.longValue()<=1){
            setExpire(key,1,TimeUnit.HOURS);
        }
        return l;

    }




    /**
     * redis提供的setNX函数，具有互斥的性质，可以用来设置一个标记判断唯一操作,值与key想通
     *
     * @param key 键名称，需要唯一
     * @return
     */
    public static boolean setNX(final String key) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX((key).getBytes(), key.getBytes());
            }
        });
    }

    /**
     * redis提供的setNX函数，具有互斥的性质，可以用来设置一个标记判断唯一操作
     *
     * @param key      键名称，需要唯一
     * @param timeout  超时时长
     * @param timeUnit 超时时长单位
     * @return
     */
    public static boolean setNX(final String key, long timeout, TimeUnit timeUnit) {
        boolean r1 = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX((key).getBytes(), key.getBytes());
            }
        });
        boolean r2 = setExpire(key, timeout, timeUnit);
        return r1 && r2;
    }

    /**
     * redis提供的setNX函数，具有互斥的性质，可以用来设置一个标记判断唯一操作,值与key想通
     *注意该方法是如果有key  是不会更新key的
     * @param key   键名称，需要唯一
     * @param value 值
     * @return
     */
    public static boolean setNX(final String key, final String value) {
        //deleteKey(key);
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX((key).getBytes(), value.getBytes());
            }
        });
    }

    /**
     * redis提供的setNX函数，具有互斥的性质，可以用来设置一个标记判断唯一操作
     *
     * @param key      键名称，需要唯一
     * @param value    值
     * @param timeout  超时时长
     * @param timeUnit 超时时长单位
     * @return
     */
    public static boolean setNX(final String key, final String value, long timeout, TimeUnit timeUnit) {
        boolean r1 = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.setNX((key).getBytes(), value.getBytes());
            }
        });
        boolean r2 = setExpire(key, timeout, timeUnit);
        return r1 && r2;
    }


    /**
     * 封装redis的set方法,用于专门设置一个String类型缓存
     *
     * @param key   键名称
     * @param value 值
     * @return
     */
    public boolean setString(final String key, final String value) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), value.getBytes());
                return true;
            }
        });
    }

    /**
     * 封装redis的set方法,用于专门设置一个String类型缓存
     *
     * @param key      键名称
     * @param value    值
     * @param timeout  超时时长
     * @param timeUnit 超时时长单位
     * @return
     */
    public boolean setString(final String key, final String value, long timeout, TimeUnit timeUnit) {
        boolean r1 = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), value.getBytes());
                return true;
            }
        });
        boolean r2 = setExpire(key, timeout, timeUnit);
        return r1 && r2;
    }


    /**
     * 封装redis的get方法，用于获取一个String类型缓存
     *
     * @param key 键名称
     * @return String类型值
     */
    public static String getString(final String key) {
        return redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] value = connection.get(key.getBytes());
                return value == null ? null : new String(value);
            }
        });
    }

    /**
     * 缓存一个对象数据
     *
     * @param key   键名称
     * @param value 对象值
     * @return
     */
    public static boolean setObject(final String key, final Object value) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), serialize(value));
                return true;
            }
        });
    }

    /**
     * 缓存一个对象数据
     *
     * @param key      键名称
     * @param value    对象值
     * @param timeout
     * @param timeUnit
     * @return
     */
    public static boolean setObject(final String key, final Object value, long timeout, TimeUnit timeUnit) {
        boolean r1 = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key.getBytes(), serialize(value));
                return true;
            }
        });
        boolean r2 = setExpire(key, timeout, timeUnit);
        return r1 && r2;
    }

    /**
     * 获取一个对象数据
     *
     * @param key 键名称
     * @param <T> 对象类型
     * @return
     */
    public static  <T extends Object> T getObject(final String key) {
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] value = connection.get(key.getBytes());
                return value == null ? null : (T) unSerialize(value);
            }
        });
    }


    /**
     * 向一个缓存集合中添加多个字符串
     *
     * @param key    键名称
     * @param values 值
     * @return
     */
    public Long sAdd(final String key, final byte[]... values) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sAdd(key.getBytes(), values);
            }
        });
    }

    /**
     * 向一个缓存集合中添加多个字符串
     *
     * @param key      键名称
     * @param timeout  过期时间
     * @param timeUnit 时间类型
     * @param values   值
     * @return
     */
    public Long sAdd(final String key, long timeout, TimeUnit timeUnit, final byte[]... values) {
        long r1 = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sAdd(key.getBytes(), values);
            }
        });
        setExpire(key, timeout, timeUnit);
        return r1;
    }

    /**
     * 通过键名称获取set值
     *
     * @param key 键名称
     */
    public Set<byte[]> sMembers(final String key) {
        return redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
            @Override
            public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sMembers(key.getBytes());
            }
        });
    }

    /**
     * 移除set中的指定元素
     *
     * @param key    键名称
     * @param values 元素集合
     * @return
     */
    public Long sRem(final String key, final byte[]... values) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sRem(key.getBytes(), values);
            }
        });
    }

    /**
     * 设置一个缓存的过期
     *
     * @param key
     * @param timeout
     * @param timeUnit
     * @return
     */
    public static boolean setExpire(String key, long timeout, TimeUnit timeUnit) {
        return redisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * 删除一个redis缓存
     *
     * @param key
     * @return
     */
    public long deleteKey(final String key) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(key.getBytes());
            }
        });
    }

    /**
     * 将对象转换为字节数组
     *
     * @param object 对象
     * @return
     */
    public static byte[] serialize(Object object) {
        try {
            return SerializeUtil.serialize(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将字节数组转换为对象
     *
     * @param bytes
     * @return
     */
    public static Object unSerialize(byte[] bytes) {
        try {
            //反序列化
            return SerializeUtil.unserialize(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
